package com.chinackts.common.log.aspect;

import com.alibaba.fastjson.JSON;
import com.chinackts.auth.repository.redis.SocialCreditCodeRepository;
import com.chinackts.common.log.model.Log;
import com.chinackts.common.log.service.LogService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.common.TemplateParserContext;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;

@Aspect
@Component
@RequiredArgsConstructor
@Slf4j
public class CusDecReqLogAspect {

    private final LogService logService;
    private final SocialCreditCodeRepository socialCreditCodeRepository;

    @Around("@annotation(cusDecReqLog)")
    public Object logExecution(ProceedingJoinPoint joinPoint, CusDecReqLog cusDecReqLog) throws Throwable {
        long start = System.currentTimeMillis();
        Object result = null;
        Throwable error = null;

        StandardEvaluationContext context = new StandardEvaluationContext();
        ExpressionParser expressionParser = new SpelExpressionParser();

        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        String[] paramNames = signature.getParameterNames();
        Object[] args = joinPoint.getArgs();

        Map<String, Object> params = new HashMap<>();
        if (paramNames != null) {
            for (int i = 0; i < paramNames.length; i++) {
                params.put(paramNames[i], args[i]);
            }
            context.setVariables(params);
        }

        String id = expressionParser.parseExpression(cusDecReqLog.id(), new TemplateParserContext()).getValue(context, String.class);

        try {
            result = joinPoint.proceed();
            return result;
        } catch (Throwable e) {
            error = e;
            throw e;
        } finally {
            try {
                doLog(id, cusDecReqLog, params, result, error, System.currentTimeMillis() - start);
            } catch (Exception logException) {
                // 避免日志异常干扰主流程
                log.warn("日志记录失败", logException);
            }
        }
    }

    private void doLog(String id,
                       CusDecReqLog cusDecReqLog,
                       Map<String, Object> params,
                       Object result,
                       Throwable error,
                       long time) {
        String orgCode = socialCreditCodeRepository.getSocialCreditCode(id);
        String requestUrl = cusDecReqLog.urlConstants();

        String responseMsg = error == null ? result.toString() : error.getMessage();

        logService.log(Log.builder()
                .s_id(id)
                .org_code(orgCode)
                .type(cusDecReqLog.type().getCode())
                .success(error == null ? "1" : "0")
                .request_url(requestUrl)
                .request_msg(JSON.toJSONString(params))
                .response_msg(responseMsg)
                .create_time(new Date())
                .time(time)
                .build());
    }

}
